<?php

namespace app\common;

use app\notify\controller\Cloud;
use think\cache\driver\Redis;

abstract class AbstractAllotManager {

    /*
    客户的基础配置信息：
    在crm系统的id，token的，蒙悦科技的rsa.publick_key,
    */
    abstract public function getYunduoCompanyId();
    abstract public function getYunduoToken();
    abstract public function getRSAPubKey();

    // 开关：是否开实验桶
    abstract public function isExperimentOn();

    //该客户是否需要入桶（某些渠道不进入蒙悦科技）
    abstract public function isCustomersToAllot($all_customers);

    //该销售是否参与分配名片
    abstract public function isSaleToAllot($sale);

    //是否为实验桶数据，
    abstract public function getBucketOfCust($cust, $experiment_percentage);

    //默认分配规则
    abstract public function allotSaleForCustomer($cust, $sales, $bucket);

    //实验桶的百分占比，30%
    abstract public function getExperimentPercentage();

    // 日志文件地址
    abstract public function get_log_file() ;

    //数据入库
    abstract  public function getEncryAttr();
    //获取分配规则信息
    abstract  public function getAllotRuleInfo();
    //初始化工作
    public function init() {

    }
    public function md5_rsa_encode($customers, $rsa_public_key) {
        $key_res = openssl_pkey_get_public($rsa_public_key);
        foreach ($customers as $customer) {
            $phone = $customer->phone;
            $md5_str = md5($phone);
            openssl_public_encrypt($md5_str, $encrypted, $key_res);
            $encrypted = base64_encode($encrypted);
            $customer->md5_str = $md5_str;
            $customer->rsa_str = $encrypted;
        }
        return $customers;
    }

    // 除用户特殊属性，phone，md5
    function removeProperties($customers) {
        foreach ($customers as $customer) {
            unset($customer->md5_str);
            unset($customer->phone);
        }
        return $customers;
    }

    private function logger($context, $level="info", $_new_trace_id=false) {
        $logFile=$this->get_log_file();
        if(is_file($logFile)){
            $handle=fopen($logFile,'a');
            fwrite($handle,$context);
        }else{
            $handle=fopen($logFile,'w');
            fwrite($handle,$context);
        }
        fclose($handle);


    }

    protected function isAlloted($cust) {
        return false;
    }

    /*拉取数据, TODO mock*/
    private function postData($param, $header, $url) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);  //设置url
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在 设为0表示不检查证书 设为1表示检查证书中是否有CN(common name)字段 设为2表示在1的基础上校验当前的域名是否与CN匹配
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST'); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_setopt($curl, CURLOPT_TIMEOUT, 0); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回


        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    /*获取签名*/
    private function genSignature($token, $ts) {
        $company_id = $this->getYunduoCompanyId();
        $key = $ts . $company_id;
        return bin2hex(hash_hmac("sha256", $token, $key, true));
    }

    private function callYunduo($url, $params) {
        $ts = time();
        $token = $this->getYunduoToken();
        $signature = $this->genSignature($token, $ts);

        /*header信息不能有空格*/
        $header = array(
            'Content-Type:application/json',
            'accessToken:' . $token,
            'timestamp:' . $ts,
            'signature:' . $signature,
        );

        /*执行拉取*/
        $res = $this->postData($params, $header, $url);
        $res = json_decode($res,true);
        return $res;
    }

    // TODO: 调用云朵接口，查询过去一段时间内新增的名片
    private function getAllCustomersFromYunduo($time_begin, $time_now) {
        /*每页50条数据*/
        $company_id = $this->getYunduoCompanyId();
        $url = "https://outcrmapi.yunduocrm.com/api/dataSync/findCustomerByCreateTime/{$company_id}?router_id={$company_id}";

        $p = 1;
        $all_data = [];
        while(true) {
            /*参数信息*/
            $params = array();
            $params['beginTime'] = date('Y-m-d H:i:s', $time_begin);
            $params['endTime'] = date('Y-m-d H:i:s', $time_now);
            $params['pageNum'] = $p;//分页
            $params = json_encode($params);

            $res = $this->callYunduo($url, $params);
//            dump($res);
            $this->logger("customerList.respageNum.$p = " . json_encode($res) . "\n");
            if (!array_key_exists("code", $res) || $res['code'] != 200 || !array_key_exists('customerList', $res['data'])) {
                break;
            }
            $all_data=$this->pushDataAndEncode($all_data,$res['data']['customerList']);
            $p++;
        }
//        dump($all_data);
        return $all_data;
//        return $this->md5_rsa_encode($all_data, $this->getRSAPubKey());
    }
    private function pushDataAndEncode($all_data,$list,$encry=true){
        $encryAttr=$this->getEncryAttr();
        $rsa_public_key=$this->getRSAPubKey();
//        dump($list);
        foreach($list as $v){
            //加密
            if($encry){
                $key_res = openssl_pkey_get_public($rsa_public_key);
                foreach ($encryAttr as $ve){
                    if(isset($v[$ve])){
                        $val = $v[$ve];
                        $md5_str = md5($val);
                        openssl_public_encrypt($md5_str, $encrypted, $key_res);
                        $encrypted = base64_encode($encrypted);
                        $v[$ve."_md5_str"]= $md5_str;
                        $v[$ve."_rsa_str"]= $encrypted;
//                    $v[$ve]=$md5_str;
                    }
                }
            }
            $all_data[]=$v;
        }
        return $all_data;
    }
    // TODO: 从所有线索中，找到实验对比范围内的待分配的销售
    private function findSalesToAllot($sales) {
        foreach ($sales as $k=>$v){
            if($v['onlineStatus']==3 || $v['allotStatus']==2 || $v['everydayToplimit']-$v['realAllotNum']<=0 || in_array($v['saleLevelName'],["G"])){
                unset($sales[$k]);
            }
        }
        return $sales;
    }

    /*获取待分配用户线索*/
    private function findCustomersToAllot($all_customers) {
        return  $this->isCustomersToAllot($all_customers);
//        return $result;
    }

    //获取所有的在线？销售人员消息
    private function getAllSalesFromYunduo() {
        $company_id = $this->getYunduoCompanyId();
        $allotRule=$this->getAllotRuleInfo();
        $data = array();
        $url = "https://outcrmapi.yunduocrm.com/api/dataSync/getSalesInfoOfAutoAllotRuleConfig/{$company_id}?router_id={$company_id}";
        $data['auto_allot_rule_name'] = $allotRule['name'];
        $data['auto_allot_rule_type'] = $allotRule['type'];
        $params=json_encode($data);
        $p = 1;
        $res = $this->callYunduo($url, $params);
        $this->logger("salesList.res.pageNum.$p = " . json_encode($res) . "\n");
        if (!array_key_exists("code", $res) || $res['code'] != 200 || !array_key_exists('data', $res)) {
            return false;
        }
        $this->initSaler($res['data']);
        return $res['data'];
    }
    private function initSaler($list)
    {
        $redis =new Redis(config('redis'));
        foreach ($list as $v){
            if(!$v) continue;
            $keys=$v['saleId'];
            if(!$redis->get($keys)){
                /*分桶缓存数据不存在重新缓存*/
                $data=array();
                $use=$v['everydayToplimit']-$v['realAllotNum'];
                $data[0]=ceil($use/2);//对照桶
                $data[1]=$use-$data[0];//实验桶
                $timeLimit=strtotime("+1 days")-time();

                $redis->set($keys,json_encode($data),$timeLimit);
            }
        }
    }

    protected function getAllotLimitOfBuckets($sales, $experiment_percentage) {
        return $this->isSaleToAllot($sales);
        $redis=new Redis(config('redis'));
        foreach ($sales as $k=>$v){
            $keys=$sales['saleId'];
            $allotRecord=$redis->get($keys);
            if($allotRecord){
                $allotRecord=json_decode($allotRecord);
                if($allotRecord[1]<=0){
                    //查询实验桶

                }
            }
        }

        return $sales;
    }


    private function sendStorageCustomerDataToMengyue($customers, $sales) {

    }

    //调用蒙悦接口，返回最匹配的销售
    private function findBestSaleForCustFromMengyue($customers, $sales, $target_cust, $target_sale, $bucket) {
        $customers = $this->removeProperties($customers);
        $this->logger("findBestSaleForCustFromMengyue.start customer: " . json_encode($customers) . ", sales: " . json_encode($sales)
            . ", target_cust: " . json_encode($target_cust) . ", target_sale: " . json_encode($target_sale) . ", bucket: $bucket");
        if($bucket == 0) {
            return Array($target_cust, $target_sale);
        }
        return Array($target_cust, $target_sale);
    }

    //执行分配，并回写crm
    // 需要修改本地销售状态
    private function doAllot($cust, $sale, $bucket) {
        if($this->setBindSalerAngCustomer($cust,$sale)){
            $redis=new Redis(config('redis'));
            $redis->set('lastSaler',$sale);
            $saleRecord=$redis->get($sale['saleId']);
            $saleRecord=json_decode($saleRecord);
            $saleRecord[$bucket]-=1;
            $redis->set($sale['saleId'],json_encode($saleRecord));
        }else{
            return false;
        }

    }

    private function setBindSalerAngCustomer($cust,$sale){
        $url="https://outcrmapi.yunduocrm.com/api/dataSync/customerTransferOrAllot/2015?router_id=2015";
        $data=array();
        $allotRule=$this->getAllotRuleInfo();
        $data['customer_id'] = $cust['uniqueCustomerId'];
        $data['sale_id'] = $sale['saleId'];
        $data['allow_transfer'] = 1;
        $data['auto_allot_rule_name'] = $allotRule['name'];
        $data['auto_allot_rule_type'] = $allotRule['type'];
        $params=json_encode($data);
        $res = $this->callYunduo($url, $params);
        $this->logger("bingSaler.res = " . json_encode($res) . "\n");
        if (!array_key_exists("code", $res) || $res['code'] != 200) {
            return false;
        }
        return true;

    }

    //剔除已分配的客户
    private function filterOutAllotedCustomer($customers, $alloted_cust) {

    }

    /**
     * 实时分配
     * @param $time_offset time offset
     */
    public function runRealtimeAllocation($time_offset) {
        $this->logger("runRealtimeAllocation start.");
        $this->init();
        $experiment_percent = $this->getExperimentPercentage();

        // 线索list
        $time_now = time();
        $time_begin = $time_now - $time_offset;
        $all_customers = $this->getAllCustomersFromYunduo($time_begin, $time_now);
//        halt($all_customers);
        $this->logger("all_customers =" . json_encode($all_customers), "debug");
        //取出需要分配的用户
        $customers = $this->findCustomersToAllot($all_customers);
        $this->logger("findCustomersToAllot.customers =" . json_encode($customers));
        if(empty($customers)){
            return false;
        }
        // 销售list
        $all_sales = $this->getAllSalesFromYunduo();
        $this->logger("all_sales =" . json_encode($all_sales));
//          取出可分配销售
        $sales = $this->findSalesToAllot($all_sales);
        $this->logger("findSalesToAllot_sales =" . json_encode($sales));

        /*取出可分配分桶销售*/
        $sales = $this->getAllotLimitOfBuckets($sales, $experiment_percent);
        $this->logger("getAllotLimitOfBuckets_sales = " . json_encode($sales));
        if(empty($sales)){
            return false;
        }
        foreach ($customers as $cust) {
            $this->logger("cuctomer id = ".$cust['id'].", experiment_percent = $experiment_percent", "info", true);

            // 分桶
            $bucket = $this->getBucketOfCust($cust, $experiment_percent);
            $this->logger("cuctomer id = ".$cust['id'].", experiment_percent = $experiment_percent, bucket = $bucket");

            //预分配
            $default_sale = $this->allotSaleForCustomer($cust, $sales, /*bucket=*/$bucket);
            $this->logger("default_sale: $default_sale");

            // 没有销售放库存
            if ($default_sale == null) { // 没有找到可以分配的销售，线索进库存
                $this->logger('no default_sale left. ');
                continue;
            }

            if ($bucket == 0) {
                // 对照桶
                if ($this->isExperimentOn()) {
                    $this->findBestSaleForCustFromMengyue(
                    /*customers=*/[$cust],
                        $sales,
                        /*target_customer=*/$cust,
                        /*target_sale=*/$default_sale,
                        /*bucket=*/$bucket);
                }
                $this->doAllot($cust, $default_sale, /*bucket=*/$bucket);
            } else {
                // 实验桶
                if ($this->isExperimentOn()) {
                    list($mengyue_customer, $mengyue_sale) = $this->findBestSaleForCustFromMengyue(
                    /*customers=*/[$cust],
                        $sales,
                        /*target_customer=*/$cust,
                        /*target_sale=*/$default_sale,
                        /*bucket=*/$bucket);
                    $default_sale = $mengyue_sale;
                    $this->logger("findBestSaleForCustFromMengyue.result customer: " . json_encode($cust) . ", default_sale: $default_sale, bucket: $bucket, mengyue_sale: ". json_encode($mengyue_sale));
                }

                // 执行分配结果
                if ($default_sale != null) {
                    $this->doAllot($cust, $default_sale, /*bucket=*/$bucket);
                } else {
                    $this->logger("mengyue error. customer: " . json_encode($cust) . "sales: " . json_encode($sales)
                        . ", default_sale: $default_sale, bucket: $bucket, mengyue_sale: ". json_encode($mengyue_sale), "error");
                }
            }
        }
        $this->logger("done.");
    }

    //库存分配 TODO
    public function runStorageAlloction() {

    }
};